/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package metodos;

import datos.Datos;
import java.util.ArrayList;
import java.util.HashSet;
import javax.swing.JOptionPane;


public class K_means {

    Datos datos; //contiene todos los datos con los que se van a trabajar
    private ArrayList<ArrayList<Double>> lista;
    private ArrayList<Double> aux;
    private ArrayList<String> valoresClase;
    private ArrayList<ArrayList<Double>> centroides;
    private ArrayList<Integer> columnasEliminadas;
    ArrayList<ArrayList<ArrayList<Double>>> grupos;
    ArrayList<ArrayList<Double>> distancias;
    private ArrayList<String> titulosNuevos;

    public K_means(Datos data) {
        this.datos = data;
        valoresClase = new ArrayList<String>();
        aux = new ArrayList<Double>();
        grupos = new ArrayList<ArrayList<ArrayList<Double>>>();
        distancias = new ArrayList<ArrayList<Double>>();
        columnasEliminadas = new ArrayList<Integer>();
        titulosNuevos = new ArrayList<String>();
        lista = new ArrayList<ArrayList<Double>>();

    }

    public Datos getDatos() {
        return datos;
    }

    public void setDatos(Datos datos) {
        this.datos = datos;
    }

    public ArrayList<Double> getAux() {
        return aux;
    }

    public void setAux(ArrayList<Double> aux) {
        this.aux = aux;
    }

    public ArrayList<ArrayList<Double>> getLista() {
        return lista;
    }

    public void setLista(ArrayList<ArrayList<Double>> lista) {
        this.lista = lista;
    }

    public ArrayList<String> getValoresClase() {
        return valoresClase;
    }

    public void setValoresClase(ArrayList<String> valoresClase) {
        this.valoresClase = valoresClase;
    }

    public boolean Calcular() {
        boolean permitir = true;
        aux = new ArrayList<Double>();
        int iteracion = 0;
        double auxOrdenamiento = 0;
        int posicionMenorC = 0;
        int posicionMenorF = 0;
        int gc = 0;
        int gf = 0;
        reconocimientoNumeros();
        ArrayList<ArrayList<Double>> listaAuxiliar;

        if (!lista.isEmpty()) {

            JOptionPane.showMessageDialog(null, "Lista COLUMNAS " + lista.size());

            //JOptionPane.showMessageDialog(null, "Lista en 0 " + lista.size());

            while (iteracion < 100) {//para no crear ciclos infinitos
                boolean gameover = true;//
                int numGrupos = conteoValorClase();

                //System.out.print("NUMERO DE GRUPOS" + numGrupos + "\n");

                if (iteracion == 0) {

                    getCentroides(iteracion);
                    distancias = new ArrayList<ArrayList<Double>>();
                    calcularDistancia(lista);//Funcion revisada con éxito


                    for (int t = 0; t < numGrupos; t++) {
                        grupos.add(new ArrayList<ArrayList<Double>>());
                    }
                    //System.out.print("GRUPOS : " + grupos.size() + "\n");
                    for (int i = 0; i < grupos.size(); i++) {
                        for (int c = 0; c < lista.size(); c++) {
                            grupos.get(i).add(new ArrayList<Double>());
                        }
                    }
                    System.out.print("GRUPOS COLUMNAS : " + grupos.get(0).size() + "\n");
                    for (int f = 0; f < distancias.get(0).size(); f++) {//filas
                        aux = new ArrayList<Double>();
                        for (int c = 0; c < distancias.size(); c++) {//columnas 
                            double auxiliar = distancias.get(c).get(f);

                            if (c == 0) {
                                auxOrdenamiento = distancias.get(c).get(f);
                                posicionMenorC = c;
                                posicionMenorF = f;
                            } else {
                                if (auxiliar <= auxOrdenamiento) {
                                    auxOrdenamiento = distancias.get(c).get(f);
                                    posicionMenorC = c;
                                    posicionMenorF = f;
                                }
                            }
//                            if (c < lista.size()) {//no se puede manejar el indice de la distancia con el de la lista


//                            }

                        }
                        for (int y = 0; y < lista.size(); y++) {
                            aux.add(lista.get(y).get(f));
                        }

                        //System.out.print("Posicion " + posicionMenorC + "\n");

                        crearGrupos(posicionMenorC, aux);
                        aux.clear();
                        auxOrdenamiento = 0;
                    }
                } else {
                    posicionMenorC = 0;
                    posicionMenorF = 0;
                    aux = new ArrayList<Double>();
                    getCentroides(iteracion);
                    listaAuxiliar = new ArrayList<ArrayList<Double>>();
                    distancias = new ArrayList<ArrayList<Double>>();
                    ArrayList<Double> arrayList = new ArrayList<Double>();
                    ArrayList<Integer> grupoOrigen = new ArrayList<Integer>();
                    ArrayList<Integer> grupoDestino = new ArrayList<Integer>();
                    ArrayList<Integer> filasGO = new ArrayList<Integer>();
                    ArrayList<ArrayList<Double>> elementosBusqueda = new ArrayList<ArrayList<Double>>();


                    // calcula la distancia por grupos
                    for (int w = 0; w < grupos.get(0).size(); w++) {//columnas


                        for (int d = 0; d < grupos.size(); d++) {//cantidad grupos

                            for (int r = 0; r < grupos.get(d).get(w).size(); r++) {//filas
                                arrayList.add(grupos.get(d).get(w).get(r));
                            }

                        }

                        listaAuxiliar.add(arrayList);
                        arrayList = new ArrayList<Double>();

                    }
                    calcularDistancia(listaAuxiliar);
                    int filaActual = 0;
                    for (int g = 0; g < grupos.size(); g++) {
                        //cantidad de grupos
                        gc = 0;
                        gf = 0;
                        for (int f = filaActual; f < distancias.get(0).size(); f++) {//filas
                            for (int c = 0; c < distancias.size(); c++) {//columnas
                                double auxiliar = distancias.get(c).get(f);

                                if (c == 0) {
                                    auxOrdenamiento = distancias.get(c).get(f);
                                    posicionMenorC = c;
                                    posicionMenorF = gf;
                                } else {
                                    if (auxiliar <= auxOrdenamiento) {
                                        auxOrdenamiento = distancias.get(c).get(f);
                                        posicionMenorC = c;
                                        posicionMenorF = gf;
                                    }
                                }
                            }
                            if (posicionMenorC != g) {
                                gameover = false;
                                grupoOrigen.add(g);
                                grupoDestino.add(posicionMenorC);
                                filasGO.add(gf);

                                for (int c = 0; c < grupos.get(g).size(); c++) {
                                    aux.add(grupos.get(g).get(c).get(gf));
                                }
                                elementosBusqueda.add(aux);

                            }
                            aux = new ArrayList<Double>();
                            gc++;
                            gf++;
                            auxOrdenamiento = 0;
                            if (gf < grupos.get(g).get(0).size()) {
                                continue;
                            } else {
                                filaActual = f + 1;
                                break;
                            }

                        }
                    }

                    organizarGrupos(grupoOrigen, grupoDestino, filasGO, elementosBusqueda);//Organiza los grupos


                }//fin else           
                if (gameover && iteracion != 0) {
                    eliminarDatosNumericos();
                    agregarNuevaColumna();
                    armarSalida();
                    break;
                }

                iteracion++;

            }
            if (iteracion == 99) {
                JOptionPane.showMessageDialog(null, "Se hicieron 100 iteraciones y no hubo agrupamiento !");
            }
        } else {
            permitir = false;
            System.out.println("No existen datos númericos");

        }

        return permitir;
    }

    public void eliminarDatosNumericos() {
        ArrayList<String> nombrecolumnasE = new ArrayList<String>();

        for (int t = 0; t < datos.getTitulos().length; t++) {
            titulosNuevos.add(datos.getTitulos()[t]);
        }
        for (int r = 0; r < columnasEliminadas.size(); r++) {
            int position = columnasEliminadas.get(r);
            String cadena = titulosNuevos.get(position);
            nombrecolumnasE.add(cadena);
        }
        for (int cd = 0; cd < nombrecolumnasE.size(); cd++) {
            for (int e = 0; e < titulosNuevos.size(); e++) {
                String cadena = titulosNuevos.get(e);

                if (nombrecolumnasE.get(cd).equalsIgnoreCase(cadena))
                    titulosNuevos.remove(e);
            }
        }

        ArrayList<String> elementosSeleccionados = new ArrayList<String>();
        ArrayList<ArrayList<String>> datosNuevos = new ArrayList<ArrayList<String>>();
        boolean datosCorrecto = false;

        for (int f = 0; f < datos.getLista().size(); f++) {
            for (int c = 0; c < datos.getLista().get(f).size(); c++) {
                datosCorrecto = false;
                try {
                    String elemento = datos.getLista().get(f).get(c);
                    double d = Double.parseDouble(elemento);

                } catch (NumberFormatException e) {
                    elementosSeleccionados.add(datos.getLista().get(f).get(c));
                    datosCorrecto = true;
                    continue;
                }
            }
            if (datosCorrecto) {
                datosNuevos.add(elementosSeleccionados);
                elementosSeleccionados = new ArrayList<String>();
            }

        }
        datos.setLista(datosNuevos);
    }

    public void agregarNuevaColumna() {
        titulosNuevos.add("NuevoAtributo");
        int cantTitulos = titulosNuevos.size();

        String[] titulos = new String[cantTitulos];

        for (int i = 0; i < cantTitulos; i++) {
            titulos[i] = "";
            titulos[i] = titulosNuevos.get(i);
            System.out.println(titulos[i]);
        }
        
        datos.setTitulos(titulos);
    }

    public void armarSalida() {//mejorar la funcion

        ArrayList<String> valoresNuevos = new ArrayList<String>();
        boolean grupoEncontrado;
        for (int l = 0; l < lista.get(0).size(); l++) {//filas lista
            grupoEncontrado = false;
            int contador = 0;
//            for (int j = 0; j < lista.size(); j++) {//columnas 
            for (int g = 0; g < grupos.size(); g++) {//grupos
                for (int r = 0; r < grupos.get(g).get(0).size(); r++) {//filas
                    double numeroGrupoB = grupos.get(g).get(0).get(r);
                    double numeroListaGeneral = lista.get(0).get(l);
                    if (numeroGrupoB == numeroListaGeneral) {
                        contador++;
                        for (int c = 1; c < lista.size(); c++) {
                            double numeroGrupoB2 = grupos.get(g).get(c).get(r);
                            double numeroListaGeneral2 = lista.get(c).get(l);
                            if (numeroGrupoB2 == numeroListaGeneral2) {
                                contador++;
                                grupoEncontrado = true;

                            } else {
                                contador = 0;
                                grupoEncontrado = false;
                                break;

                            }
                        }
                    }
                    if (contador == grupos.get(g).size()) {
                        break;
                    }
                }
                if (grupoEncontrado) {
                    valoresNuevos.add("Grupo" + (g + 1));
                    break;
                }

            }
//            }

        }
//        datos.getLista().add(valoresNuevos);//Valores nuevos
        for (int w = 0; w < valoresNuevos.size(); w++) {//filas
            String cadena = valoresNuevos.get(w);
            datos.getLista().get(w).add(cadena);
        }

    }

    public void calcularDistancia(ArrayList<ArrayList<Double>> elemList) {

//        distancias = new ArrayList<ArrayList<Double>>();

//        aux = new ArrayList<Double>();
        double auxR = 0;
        double auxC = 0;
        double sumatoria = 0;
        double raiz = 0;
        int maxIteracion = conteoValorClase();
        int contador = 0;
//
        //System.out.print("Columnas elem : " + elemList.size() + "\n");
        //System.out.print("Filas : " + elemList.get(0).size() + "\n");
        while (contador < maxIteracion) {
            aux = new ArrayList<Double>();
            for (int j = 0; j < elemList.get(0).size(); j++) {//elementos filas

                for (int i = 0; i < elemList.size(); i++) {//columnas
                    Double get = elemList.get(i).get(j);

                    auxR = centroides.get(contador).get(i) - get;
                    auxC = Math.pow(auxR, 2);
                    sumatoria += auxC;
                }
                raiz = Math.sqrt(sumatoria);
                aux.add(raiz);
                sumatoria = 0;
            }
            distancias.add(aux);
            contador++;

        }
        //System.out.print("----------------------DISTANCIAS---------------------");
        for (int k = 0; k < distancias.size(); k++) {
            //System.out.print("Columna " + (k + 1) + " :\n");
            for (int l = 0; l < distancias.get(0).size(); l++) {
                //System.out.print("" + distancias.get(k).get(l) + "\n");
            }
        }
    }

    public int conteoValorClase() {
        int indice = getIndiceClase();

        for (int i = 0; i < getDatos().getLista().size(); i++) {
            getValoresClase().add(getDatos().getLista().get(i).get(indice));
        }
        //Borro lo que se repita
        HashSet hs = new HashSet();
        hs.addAll(getValoresClase());
        getValoresClase().clear();
        getValoresClase().addAll(hs);
        hs.clear();

        return getValoresClase().size();

    }

    public void getCentroides(int iteracion) {
        aux = new ArrayList<Double>();
        centroides = new ArrayList<ArrayList<Double>>();//centroides nuevos
        int numCentroides = conteoValorClase();
        int cantRegistro = datos.getLista().size();
        int porcion = 0;// establer la distribucion de los centroides

        if (cantRegistro % numCentroides == 0) {
            porcion = (cantRegistro / numCentroides) - 2;
        } else {
            porcion = (cantRegistro / numCentroides);
        }
        //System.out.print("--" + cantRegistro + "---");
        //System.out.print("--" + porcion + "---");
        if (iteracion == 0) {
            int auxp = 0;

            for (int i = 0; i < numCentroides; i++) {
                auxp += porcion;
                aux = new ArrayList<Double>();
                for (int j = 0; j < lista.size(); j++) {//columnas
//                    while(auxp<cantRegistro){

                    aux.add(lista.get(j).get(auxp));
//                    }
                }
//                auxp = 0;

                centroides.add(aux);
            }
        } else {
            double sumatoria = 0;
            double promedio = 0;


            for (int g = 0; g < grupos.size(); g++) {//grupos
                for (int c = 0; c < grupos.get(g).size(); c++) {//columnas
                    for (int r = 0; r < grupos.get(g).get(c).size(); r++) {//elementos
                        sumatoria += grupos.get(g).get(c).get(r);
                    }
                    promedio = sumatoria / grupos.get(g).get(c).size();
                    aux.add(promedio);
                    sumatoria = 0;

                }
                centroides.add(aux);
                aux = new ArrayList<Double>();
                sumatoria = 0;
                promedio = 0;
            }

        }
        for (int i = 0; i < centroides.size(); i++) {
            String salida = "";
            for (int j = 0; j < centroides.get(0).size(); j++) {
                salida += " " + centroides.get(i).get(j);
            }
            //System.out.print("Centroide " + (i + 1) + " :" + salida);
        }
    }
    //verifica que columnas tienen numero y crea una matriz solo con esas columnas y filas

    public void reconocimientoNumeros() {//extrae las columnas númericas
//        System.out.print("Columnas" + datos.getLista().size());
//        System.out.print("Filas" + datos.getLista().get(0).size());
//        System.out.print("\n");
//        JOptionPane.showMessageDialog(null, "entrooo");
//        System.out.print("columnas : "+datos.getLista().get(0).size()+"\n");
//        System.out.print("Filas : "+datos.getLista().size()+"\n");


        for (int i = 0; i < datos.getLista().get(0).size(); i++) {//columnas
            aux = new ArrayList<Double>();
            boolean columnaELiminar = false;
            for (int j = 0; j < datos.getLista().size(); j++) {// filas
                double num;
                try {
                    num = Double.parseDouble(datos.getLista().get(j).get(i));

                } catch (NumberFormatException formatException) {
                    aux.clear();
                    columnaELiminar = true;

                    break;
                }
                aux.add(num);
            }
            if (!aux.isEmpty()) {
                lista.add(aux);
            }
            if (!columnaELiminar) {
                columnasEliminadas.add(i);
            }


        }
    }
    //IMPORTANTE solo se va entrar en esta funcion una sola vez

    public void crearGrupos(int grupo, ArrayList<Double> elementos) {
        System.out.print("CrearGrupo " + elementos.size());
        for (int e = 0; e < elementos.size(); e++) {
            Double elemento = elementos.get(e);
            grupos.get(grupo).get(e).add(elemento);
        }
    }

    //Mueve los registro a cada grupo, Funciones:add - Mover
    public void organizarGrupos(ArrayList<Integer> grupoOrigen, ArrayList<Integer> grupoDestino, ArrayList<Integer> fila, ArrayList<ArrayList<Double>> elementosBusqueda) {
        boolean grupoEncontrado = false;
        int contador = 0;
        int filaEliminar = 0;
        for (int i = 0; i < grupoDestino.size(); i++) {

//            for (int e = 0; e < elementosBusqueda.get(i).size(); e++) { //columnas
            for (int r = 0; r < grupos.get(grupoOrigen.get(i)).get(0).size(); r++) {//filas
                contador = 0;
                if (grupos.get(grupoOrigen.get(i)).get(0).get(r) == elementosBusqueda.get(i).get(0)) {
                    contador++;
                    for (int c = 1; c < elementosBusqueda.get(i).size(); c++) {//columnas

                        if (grupos.get(grupoOrigen.get(i)).get(c).get(r) == elementosBusqueda.get(i).get(c)) {
                            grupoEncontrado = true;
                            contador++;


                        } else {
                            grupoEncontrado = false;
                            contador = 0;
                            break;

                        }
                    }
                }
                if (contador == elementosBusqueda.get(0).size()) {
                    filaEliminar = r;
                    break;
                }
            }

            for (int t = 0; t < elementosBusqueda.get(0).size(); t++) {
                double valor = elementosBusqueda.get(i).get(t);
                grupos.get(grupoOrigen.get(i)).get(t).remove(filaEliminar);
                grupos.get(grupoDestino.get(i)).get(t).add(valor);
            }


        }
        for (int gr = 0; gr < grupos.size(); gr++) {
            System.out.println("\n GRUPO #" + (gr + 1));

            for (int fl = 0; fl < grupos.get(gr).get(0).size(); fl++) {
                for (int cl = 0; cl < grupos.get(gr).size(); cl++) {
                    System.out.print(" " + grupos.get(gr).get(cl).get(fl));
                }
                System.out.print("\n");
            }
        }
    }

    public ArrayList<String> getTitulos() {
        int a = getIndiceClase();
        ArrayList<String> auxiliarTitulos = new ArrayList<String>();

        for (int i = 0; i < datos.getTitulos().length; i++) {
            if (i != a) {
                auxiliarTitulos.add(datos.getTitulos()[i]);
            }
        }

        return auxiliarTitulos;
    }
    //Calcula el indice de la clase seleccionada por el usuario

    public int getIndiceClase() {

        for (int i = 0; i < getDatos().getTitulos().length; i++) {

            if (getDatos().getNombreClase().equals(getDatos().getTitulos()[i])) {
                return i;
            }
        }

        return 0;
    }
}
